#include "GlSpriteDescriptionManager.h"

#include <algorithm>
#include <cstring>
#include "GlSpriteDescription.h"

namespace GraphicLibrary
{
	SpriteDescriptionManager *SpriteDescriptionManager::msSingleton(0);

	//--------------------------------------------------
	SpriteDescriptionManager::~SpriteDescriptionManager()
	{
		for(SpriteDescriptionArray::iterator i = mDescriptions.begin(); i != mDescriptions.end(); ++i)
			delete *i;
	}

	//--------------------------------------------------
	void SpriteDescriptionManager::addDescription(const char *descFileName)
	{
		tinyxml2::XMLDocument doc;
		doc.LoadFile(descFileName);
		const tinyxml2::XMLElement *sprites = doc.FirstChildElement("sprites");
		if(!sprites)
			throw ElNotSpriteDescriptionDocument;
		const tinyxml2::XMLElement *sprite = sprites->FirstChildElement("sprite");
		while(sprite)
		{
			add(sprite);
			sprite = sprite->NextSiblingElement("sprite");
		}
	}

	//--------------------------------------------------
	const SpriteDescription* SpriteDescriptionManager::getDesc(const char *name) const
	{
		SpriteDescriptionArray::iterator i = findPlace(name);
		if(i != mDescriptions.end() && !strcmp((*i)->getName(), name))
			return *i;
		return 0;
	}

	//--------------------------------------------------
	SpriteDescriptionManager::SpriteDescriptionArray::iterator SpriteDescriptionManager::findPlace(const char *name) const
	{
		class Less_NameOrder
		{
			public:
				inline bool operator () (const SpriteDescription *left, const SpriteDescription *right) const	{return strcmp(left->getName(), right->getName()) < 0;}
				inline bool operator () (const SpriteDescription *left, const char *right) const				{return strcmp(left->getName(), right) < 0;}
				inline bool operator () (const char *left, const SpriteDescription *right) const				{return strcmp(left, right->getName()) < 0;}
		};
		SpriteDescriptionArray::iterator i = std::lower_bound(const_cast<SpriteDescriptionManager*>(this)->mDescriptions.begin(), const_cast<SpriteDescriptionManager*>(this)->mDescriptions.end(), name, Less_NameOrder());
		return i;
	}

	//--------------------------------------------------
	void SpriteDescriptionManager::add(const tinyxml2::XMLElement *sprite)
	{
		const char *name = sprite->Attribute("name");
		const char *texFileName = sprite->Attribute("file");
		float width = getFloat(sprite, "width");
		float height = getFloat(sprite, "height");
		float playTimeInSeconds = getFloat(sprite, "playTimeInSec");

		SpriteDescriptionArray::iterator i = findPlace(name);
		if(i == mDescriptions.end() || strcmp((*i)->getName(), name))
		{
			SpriteDescription *desc = new SpriteDescription(name, texFileName, Size(width, height), playTimeInSeconds);
			try
			{
				fillDescription(desc, sprite);
			}
			catch(ExceptionList e)
			{
				delete desc;
				throw e;
			}
			mDescriptions.insert(i, desc);
		}
		else
			throw ElDuplicatedSimbol;
	}

	//--------------------------------------------------
	float SpriteDescriptionManager::getFloat(const tinyxml2::XMLElement *tag, const char *attrib) const
	{
		const char *a = tag->Attribute(attrib);
		float f = 0.0f;
		sscanf(a, "%f", &f);
		return f;
	}

	//--------------------------------------------------
	void SpriteDescriptionManager::fillDescription(SpriteDescription *desc, const tinyxml2::XMLElement *sprite) const
	{
		const tinyxml2::XMLElement *frame = sprite->FirstChildElement("frame");
		if(!frame)
			throw ElIncorrectSpriteDescription_MissingFrameDesc;
		while(frame)
		{
			float x = getFloat(frame, "x");
			float y = getFloat(frame, "y");
 			desc->addFrame(Position(x, y));
			frame = frame->NextSiblingElement("frame");
		}
	}
}
